<div id="app"></div>
<!-- vue 简单的响应式 -->
<script>
    //vdom部分
    function h(tag, props, children) {
        return {
            tag,
            props,
            children,
        };
    }

    function mount(vnode, container) {
        const {
            tag,
            props,
            children
        } = vnode;
        const el = (vnode.el = document.createElement(tag));
        if (props) {
            for (let key in props) {
                const value = props[key];
                if (key.startsWith("on")) {
                    el.addEventListener(key.slice(2).toLowerCase(), value);
                } else {
                    el.setAttribute(key, value);
                }
            }
        }
        if (children) {
            if (Array.isArray(children)) {
                children.forEach((child) => {
                    if (typeof child === "string") {
                        el.append(child);
                    } else if (typeof child === "object") {
                        mount(child, el);
                    }
                });
            } else {
                el.append(children);
            }
        }
        container.append(el);
    }

    // patch 对比新旧vdom 只替换需要替换的
    function patch(n1, n2) {
        if (n1.tag === n2.tag) {
            const el = (n2.el = n1.el);
            //diff props

            const oldProps = n1.props || {};
            const newProps = n2.props || {};
            //添加新的属性或更改原来已有但变化了的属性
            for (let key in newProps) {
                const oldValue = oldProps[key];
                const newValue = newProps[key];
                if (newValue !== oldValue) {
                    el.setAttribute(key, newValue);
                }
            }

            //移除新属性中没有的属性
            for (let key in oldProps) {
                if (!(key in newProps)) {
                    el.removeAttribute(key);
                }
            }

            //diff children
            const oldChildren = n1.children;
            const newChildren = n2.children;

            if (typeof newChildren === "string") {
                if (typeof oldChildren === "string") {
                    if (oldChildren !== newChildren) {
                        el.innerHTML = newChildren;
                    }
                }
            } else if (typeof oldChildren === "string" && Array.isArray(newChildren)) {
                el.innerHTML = "";
                newChildren.forEach((child) => mount(child, el));
            } else if (Array.isArray(oldChildren) && Array.isArray(newChildren)) {
                const minLength = Math.min(oldChildren.length, newChildren.length);
                for (let i = 0; i < minLength; i++) {
                    patch(oldChildren[i], newChildren[i]);
                }
                if (oldChildren.length === minLength) {
                    for (let i = minLength; i < newChildren.length; i++) {
                        mount(newChildren[i], el);
                    }
                } else {
                    for (let i = minLength; i < oldChildren.length; i++) {
                        el.removeChild(oldChildren[i].el);
                    }
                }
            }
        } else {
            //replace
        }
    }

    //reactivity部分
    let activeEffect = null;
    class Dep {
        subs = new Set();
        depend() {
            if (activeEffect) {
                this.subs.add(activeEffect);
            }
        }
        notify() {
            this.subs.forEach((sub) => sub());
        }
    }

    function watchEffect(effect) {
        activeEffect = effect;
        effect();
        activeEffect = null;
    }

    const targetMap = new WeakMap();

    function getDep(target, key) {
        if (!targetMap.has(target)) {
            targetMap.set(target, new Map());
        }
        const depMap = targetMap.get(target);
        if (!depMap.has(key)) {
            depMap.set(key, new Dep());
        }
        return depMap.get(key);
    }

    const reactiveHandlers = {
        get(target, key, receiver) {
            // dep
            const dep = getDep(target, key);
            dep.depend();
            return Reflect.get(target, key, receiver);
        },
        set(target, key, value, receiver) {
            const dep = getDep(target, key);
            const ret = Reflect.set(target, key, value, receiver);
            dep.notify();
            return ret;
        },
    };

    function reactive(raw) {
        return new Proxy(raw, reactiveHandlers);
    }

    //component组件实例
    //container要挂载的dom元素
    function mountApp(component, container) {
        let isMounted = false;
        let oldVdom;
        watchEffect(() => {
            if (!isMounted) {
                //第一次挂载
                oldVdom = component.render();
                mount(oldVdom, container);
                isMounted = true;
            } else {
                //数据变化,要进行更新
                const newVdom = component.render();
                patch(oldVdom, newVdom);
                oldVdom = newVdom;
            }
        });
    }

    const App = {
        data: reactive({
            count: 0,
        }),
        render() {
            return h("div", null, [
                h(
                    "div", {
                        onClick: () => App.data.count++,
                    },
                    String(this.data.count)
                ),
            ]);
        },
    };
    //一个点击自增的计数器
    mountApp(App, document.getElementById("app"));
</script>